﻿namespace EasyToolkit.Excel.BindingTemplates
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using EasyToolkit.BindingTemplates;
    using EasyToolkit.BindingTemplates.Context;
    using EasyToolkit.BindingTemplates.Definitions;
    using EasyToolkit.BindingTemplates.Views;
    using EasyToolkit.Excel.Application;
    using EasyToolkit.Excel.Application.Async;
    using EasyToolkit.Excel.BindingTemplates.Definitions;
    using EasyToolkit.Excel.ContextualMenus;
    using EasyToolkit.Tools.Extensions;
    using EasyToolkit.Tools.Log;
    using Excel = Microsoft.Office.Interop.Excel;
    using Forms = System.Windows.Forms;

    public sealed class ExcelBindingTemplateManager : IExcelBindingTemplateManager
    {
        private bool initDone;
        private bool disposed;
        private static readonly Lazy<IExcelBindingTemplateManager> instance = new Lazy<IExcelBindingTemplateManager>(() => Activator.CreateInstance(typeof(ExcelBindingTemplateManager), true) as IExcelBindingTemplateManager);
        public static IExcelBindingTemplateManager Instance { get { return instance.Value; } }
        private static object syncRoot = new object();

        #region attributes and properties
        private ILogger log = Logger.Instance;
        private Dictionary<Excel.Worksheet, List<ExcelTemplateView>> viewsBySheet = new Dictionary<Excel.Worksheet, List<ExcelTemplateView>>();
        //private Dictionary<string, ContextualMenu> menuContextItemsByName = new Dictionary<string, ContextualMenu>();
        private List<Excel.Workbook> managedWorkbooks = new List<Excel.Workbook>();

        internal ExcelApplication ExcelApplication
        { get; private set; }

        internal ExcelNotifyPropertyManager ExcelNotifyPropertyManager
        { get; private set; }
        #endregion

        #region .ctors
        private ExcelBindingTemplateManager()
        {}

        ~ExcelBindingTemplateManager()
        {
            Dispose();
        }
        #endregion

        #region private methods
        private ExcelTemplateView CreateView(Excel.Worksheet sheetContainer, string templateName, Excel.Worksheet sheetDestination, Excel.Range firstOutputCell, string clearingCellAddress)
        {
            if (sheetContainer == null)
                throw new ArgumentNullException("Template container sheet is mandatory");
            if (string.IsNullOrEmpty(templateName))
                throw new ArgumentNullException("Template rangeName is mandatory");
            if (sheetDestination == null)
                throw new ArgumentNullException("Template destination sheet is mandatory");
            if (firstOutputCell == null)
                throw new ArgumentNullException("Template first output cell is mandatory");

            Excel.Range clearingCell = null;
            if (!string.IsNullOrEmpty(clearingCellAddress))
            {
                try
                {
                    Excel.Range workingCell  = sheetDestination.Range[clearingCellAddress];
                    clearingCell = workingCell.Resize[1, 1];
                    workingCell = null;
                }
                catch
                {
                    throw new ArgumentException(string.Format("The clearing cell value '{0}' is not valid", clearingCellAddress));
                }
            }
            if (!initDone)
                Init(sheetContainer.Application);

            Excel.Range range = sheetContainer.Cells.Find(string.Format(ExcelTemplateDefinition.TEMPLATE_START_FORMAT, templateName), Type.Missing, Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, false); 
            if(range == null)
                throw new EasyToolkitException(string.Format("Cannot find the template '{0}' in sheet '{1}'", templateName.EmptyIfNull(), sheetContainer.Name.EmptyIfNull()));

            string templateDescriptionKey = string.Format("{0}-{1}", sheetContainer.Name, templateName);
            TemplateDefinition templateDefinition = BindingTemplateManager.Instance.GetTemplateDefinition(templateDescriptionKey);

            if (templateDefinition == null)
                templateDefinition = ExcelTemplateDefinition.CreateInstance(templateName, range);

            ExcelTemplateView view = new ExcelTemplateView(templateDefinition, sheetDestination, firstOutputCell, clearingCell);

            log.LogFormat(LogType.Debug, "Sheet '{0}', View '{1}'.'{2}' created.", sheetDestination.Name.EmptyIfNull(), sheetContainer.Name.EmptyIfNull(), templateName.EmptyIfNull());
            range = null;

            return view;
        }

        private void RegisterView(ExcelTemplateView view)
        {
            try
            {
                if (view != null)
                {
                    if (! viewsBySheet.ContainsKey(view.SheetDestination))
                    {
                        // Protect sheet.
                        //view.SheetDestination.Cells.Locked = false;
                        //view.SheetDestination.Protect(System.Type.Missing, false, false, false, true, true,
                        //                              true, true,
                        //                              false, false,
                        //                              true,
                        //                              false, false, false, false,
                        //                              true);
                        //view.SheetDestination.Protect(System.Type.Missing, true);

                        viewsBySheet[view.SheetDestination] = new List<ExcelTemplateView>();

                        view.SheetDestination.Change += OnSheetChange;
                        view.SheetDestination.SelectionChange += OnSelectionChange;

                        Excel.Workbook workbook = view.SheetDestination.Parent as Excel.Workbook;
                        Excel.Workbook managedWorkbook = managedWorkbooks.FirstOrDefault(w => w == workbook);
                        if (managedWorkbook == null)
                        { 
                            managedWorkbooks.Add(workbook);
                            ContextualMenuManager.Instance.RegisterWorkbook(workbook);
                            workbook.SheetActivate += OnActivateSheetViewsManagement;
                        }
                        workbook = null;
                        managedWorkbook = null;
                    }
                    viewsBySheet[view.SheetDestination].Add(view);
                }
            }
            catch (Exception ex)
            {
                throw new EasyToolkitException("View registration failed", ex);
            }
        }

        private void OnSelectionChange(Excel.Range target)
        {
            //Excel.Range realTarget = target.Cells.Count > 1 ? target.Resize[1, 1] : target;
            Excel.Range realTarget = target.Resize[1, 1];
            List<ExcelTemplateView> views;
            if (viewsBySheet.TryGetValue(realTarget.Worksheet, out views))
            {
                IEnumerable<ExcelTemplateView> viewToWorkWith = views.Select(v => v).ToList();
                foreach (ExcelTemplateView view in viewToWorkWith)
                {
                    try
                    {
                        if (view.Renderer != null && view.Renderer.OutputRange != null)
                        {
                            Excel.Range intersect = view.FirstOutputCell.Application.Intersect(view.Renderer.OutputRange, realTarget);
                            if (intersect != null)
                            {
                                IBindingContextItem contextItem = view.Renderer.GetConcernedContextItem(realTarget);
                                if (contextItem != null)
                                {
                                    IBindingContextElement currentContextElement = contextItem.Parent;
                                    IBindingContextElement targetedContextElement = currentContextElement;
                                    bool carryOn = true;
                                    do
                                    {
                                        ExcelTemplateDefinitionPart currentTemplateDefinition = currentContextElement.Parent.TemplateDefinition as ExcelTemplateDefinitionPart;
                                        MethodInfo methodInfo = currentTemplateDefinition.SelectionChanged;
                                        if (methodInfo != null)
                                        {
                                            object invokeTarget = methodInfo.IsStatic ? null : currentContextElement.DataSource;
                                            int nbrParameters = methodInfo.GetParameters().Count();
                                            object[] parameters;
                                            if (nbrParameters == 3)
                                                parameters = new object[] { realTarget, currentContextElement.DataSource, targetedContextElement.DataSource };
                                            else if(nbrParameters == 2)
                                                parameters = new object[] { currentContextElement.DataSource, targetedContextElement.DataSource };
                                            else
                                                parameters = new object[] { targetedContextElement.DataSource };
                                            
                                            carryOn = (bool)methodInfo.Invoke(invokeTarget, parameters);
                                        }
                                        if (carryOn)
                                            currentContextElement = currentContextElement.Parent.Parent;
                                    }
                                    while (carryOn && currentContextElement != null);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string message = string.Format("Sheet '{0}', Template '{1}' 'OnSelectionChange' failed: '{2}'", realTarget.Worksheet.Name, view.TemplateDefinition.Name, ex.Message);
                        log.LogException(LogType.Error, ex, message);
                    }
                }
            }
            Marshal.ReleaseComObject(realTarget);
            realTarget = null;
        }

        private IEnumerable<IContextualMenu> ManageContextualMenu(Excel.Worksheet sheet, Excel.Range range)
        {
            List<IContextualMenu> menus = new List<IContextualMenu>();
            if (sheet != null && range != null && range.Cells.Count == 1)
            {
                lock (syncRoot)
                {
                    List<ExcelTemplateView> views;
                    if (viewsBySheet.TryGetValue(sheet, out views))
                    {
                        if (views != null)
                        {
                            foreach (ExcelTemplateView view in views.Where(v => v.Renderer != null && v.Renderer.OutputRange != null).Select(v => v))
                            {
                                Excel.Range intersect = view.FirstOutputCell.Application.Intersect(view.Renderer.OutputRange, range);
                                if (intersect != null)
                                {
                                    IBindingContextItem contextItem = view.Renderer.GetConcernedContextItem(range);
                                    if (contextItem != null)
                                    {
                                        IBindingContextElement currentContextElement = contextItem.Parent;
                                        IBindingContextElement targetedContextElement = currentContextElement;
                                        do
                                        {
                                            ExcelTemplateDefinitionPart currentTemplateDefinition = currentContextElement.Parent.TemplateDefinition as ExcelTemplateDefinitionPart;
                                            if (currentTemplateDefinition.ContextualMenu != null)
                                            {
                                                ContextualMenu contextualMenu = currentTemplateDefinition.ContextualMenu as ContextualMenu;
                                                contextualMenu.SetAction(range, currentContextElement, targetedContextElement);
                                                menus.Insert(0, contextualMenu);
                                            }
                                            currentContextElement = currentContextElement.Parent.Parent;
                                        }
                                        while (currentContextElement != null);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return menus;
        }

        private void OnActivateSheetViewsManagement(object sheet)
        {
            Excel.Worksheet worksheet = sheet as Excel.Worksheet;
            try
            {
                lock (syncRoot)
                {
                    List<ExcelTemplateView> views;
                    if (viewsBySheet.TryGetValue(worksheet, out views))
                    {
                        if (views != null)
                        {
                            using (FreezeExcel freeze = new FreezeExcel(worksheet.Application))
                            {
                                foreach (ExcelTemplateView view in views)
                                {
                                    if (view.OnActivateView != null)
                                        view.OnActivateView(view);
                                    else if (view.Renderer != null && view.Renderer.OutputRange == null)
                                        view.Renderer.Render();
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string message = string.Format("Sheet '{0}': failed to render its views. {1}", worksheet == null ? string.Empty : worksheet.Name, ex.Message);
                throw new EasyToolkitException(message, ex);
            }
            finally
            {
                worksheet = null;
            }
        }

        private void OnSheetChange(Excel.Range target)
        {
            List<ExcelTemplateView> views;
            bool inError = false;
            lock (syncRoot)
            {
                if (viewsBySheet.TryGetValue(target.Worksheet, out views))
                {
                    if (views != null)
                    {
                        foreach (ExcelTemplateView view in views)
                        {
                            try
                            {
                                if (view.Renderer != null && view.Renderer.OutputRange != null)
                                {
                                    Excel.Range intersect = view.FirstOutputCell.Application.Intersect(view.Renderer.OutputRange, target);
                                    if (intersect != null)
                                    {
                                        using (FreezeExcel freeze = new FreezeExcel(target.Application))
                                        {
                                            view.Renderer.OnDataChanged(intersect);
                                            if (view.OnRangeChanged != null)
                                                view.OnRangeChanged(view);
                                            intersect = null;
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                string message = string.Format("Sheet '{0}', Template '{1}' failed to render: '{2}'",
                                                                target.Worksheet.Name, view.TemplateDefinition.Name, ex.Message);
                                log.LogException(LogType.Error, ex, message);
                                inError = true;
                            }
                        }
                    }
                }
            }

            if(inError)
            {
                Excel.Worksheet worksheet = target.Worksheet;
                string message = string.Format("Sheet '{0}', At least one View failed to render. Please, checked the log", worksheet.Name);
                throw new EasyToolkitException(message);
            }
        }

        private void Init(Excel.Application application)
        {
            ExcelApplication = new ExcelApplication(application);
            ExcelNotifyPropertyManager = new ExcelNotifyPropertyManager(ExcelApplication);

            ContextualMenuManager.Instance.ContextualMenusManager += ManageContextualMenu;
            initDone = true;
        }
        #endregion

        #region internal methods
        //internal ContextualMenu GetContextualMenu(string name)
        //{ 
        //    ContextualMenu ret = null;
        //    lock (syncRoot)
        //    {
        //        menuContextItemsByName.TryGetValue(name, out ret);
        //    }
        //    return ret;
        //}

        //internal void SetContextualMenu(ContextualMenu menu)
        //{
        //    if (menu != null)
        //    {
        //        lock (syncRoot)
        //        {
        //            menuContextItemsByName[menu.Name] = menu;
        //        }
        //    }
        //}

        internal ExcelTemplateDefinition GetTemplateDefinitionFromLink(ExcelTemplateDefinitionPart parent, TemplateLink templateLink)
        {
            try
            {
                string[] tos = templateLink.To.Split('.');
                Excel.Worksheet sheetContainer = null;
                string templateName;
                if (tos.Count() == 1)
                {
                    sheetContainer = parent.TemplateCells.Worksheet;
                    templateName = tos[0].EmptyIfNull().Trim();
                }
                else
                {
                    string worksheetContainerName = tos[0].EmptyIfNull().Trim();
                    templateName = tos[1].EmptyIfNull().Trim();

                    Excel.Worksheet parentWorkSheet = parent.TemplateCells.Worksheet;
                    Excel.Workbook workbook = parentWorkSheet.Parent as Excel.Workbook;
                    if (workbook == null)
                        throw new EasyToolkitException("Cannot retrieve the workbook that owned the template destination sheet");

                    List<Excel.Worksheet> sheets = new List<Excel.Worksheet>(workbook.Worksheets.Cast<Excel.Worksheet>());
                    sheetContainer = sheets.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name.Equals(worksheetContainerName));
                    if (sheetContainer == null)
                        throw new EasyToolkitException(string.Format("Cannot find the sheet '{0}' in the current workbook", worksheetContainerName), false);

                    Marshal.ReleaseComObject(workbook);
                    workbook = null;
                }

                string templateDescriptionKey = string.Format("{0}-{1}", sheetContainer.Name, templateName);
                ExcelTemplateDefinition templateDefinition = BindingTemplateManager.Instance.GetTemplateDefinition(templateDescriptionKey) as ExcelTemplateDefinition;
                if (templateDefinition == null)
                {
                    Excel.Range range = sheetContainer.Cells.Find(string.Format(ExcelTemplateDefinition.TEMPLATE_START_FORMAT, templateName), Type.Missing, Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, false); 
                    if (range == null)
                        throw new EasyToolkitException(string.Format("Cannot find the template '{0}' in sheet '{1}'", templateName.EmptyIfNull(), sheetContainer.Name.EmptyIfNull()));
                    templateDefinition = ExcelTemplateDefinition.CreateInstance(templateName, range);
                    range = null;
                }

                Marshal.ReleaseComObject(sheetContainer);
                sheetContainer = null;
                return templateDefinition;
            }
            catch (Exception ex)
            {
                string message = string.Format("Cannot create the template definition. {0}", ex.Message);
                throw new EasyToolkitException(message, false);
            }
        }
        #endregion

        #region IExcelBindingTemplateManager Members
        /// <summary> Implements <see cref="IExcelBindingTemplateManager.AddView"/> </summary> 
        public IExcelTemplateView AddView(Excel.Worksheet sheetContainer, string templateName, Excel.Worksheet sheetDestination, Excel.Range destinationRange, string clearingCell)
        {
            try
            {
                lock (syncRoot)
                {
                    ExcelTemplateView view = CreateView(sheetContainer, templateName, sheetDestination, destinationRange, clearingCell);
                    RegisterView(view);
                    return view;
                }
            }
            catch (Exception ex)
            {
                string message = string.Format("Sheet '{0}', cannot add the View from template definition '{1}.{2}'", sheetDestination != null ? sheetDestination.Name.EmptyIfNull() : string.Empty
                                                                                                                    , sheetContainer != null ? sheetContainer.Name.EmptyIfNull() : string.Empty
                                                                                                                    , templateName.EmptyIfNull());
                Logger.Instance.LogException(LogType.Error, ex, message);
                ExcelApplication.DisplayException(message, ex);
                return null;
            }
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.AddView"/> </summary> 
        public IExcelTemplateView AddView(string sheetContainerName, string templateName, Excel.Worksheet sheetDestination, Excel.Range destinationRange, string clearingCell)
        {
            Excel.Workbook workbook = null;
            Excel.Worksheet sheetContainer = null;
            try
            {
                if (string.IsNullOrEmpty(sheetContainerName))
                    throw new ArgumentNullException("the sheet container rangeName is mandatory");

                if (sheetDestination == null)
                    throw new ArgumentNullException("Destination sheet is mandatory");

                workbook = sheetDestination.Parent as Excel.Workbook;
                if (workbook == null)
                    throw new ApplicationException("Cannot retrieve the workbook that owned the View destination sheet");

                List<Excel.Worksheet> sheets = new List<Excel.Worksheet>(workbook.Worksheets.Cast<Excel.Worksheet>());
                sheetContainer = sheets.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name.Equals(sheetContainerName));
            }
            catch (Exception ex)
            {
                string message = string.Format("Sheet '{0}', cannot add the View from template definition '{1}.{2}'", sheetDestination != null ? sheetDestination.Name.EmptyIfNull() : string.Empty
                                                                                                                         , sheetContainerName.EmptyIfNull()
                                                                                                                         , templateName.EmptyIfNull());
                Logger.Instance.LogException(LogType.Error, ex, message);
                ExcelApplication.DisplayException(message, ex);
                return null;
            }

            IExcelTemplateView view = AddView(sheetContainer, templateName, sheetDestination, destinationRange, clearingCell);
            workbook = null;
            sheetContainer = null;
            return view;
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.RemoveView"/> </summary> 
        public void RemoveView(IExcelTemplateView view)
        {
            if (view != null)
            {
                ExcelTemplateView excelView = view as ExcelTemplateView;
                if (excelView != null)
                {
                    try
                    {
                        lock (syncRoot)
                        {
                            KeyValuePair<Excel.Worksheet, List<ExcelTemplateView>> kvp = viewsBySheet.FirstOrDefault(s => s.Value.FirstOrDefault(v => v.Equals(view)) != null);
                            if (kvp.Value != null && kvp.Value.Count > 0)
                                viewsBySheet[kvp.Key].Remove(excelView);
                            excelView.Clear();

                            if (log.GetLogLevel() == LogType.Debug)
                                log.LogFormat(LogType.Debug, "Sheet '{0}', View '{1}' from '{2}' removed.", excelView.SheetDestination.Name, excelView.Ident, excelView.TemplateDefinition.Name);
                            BindingTemplateManager.Instance.RemoveView(excelView);

                        }
                    }
                    catch (Exception ex)
                    {
                        string message = "Remove View failed.";
                        Logger.Instance.LogException(LogType.Error, ex, message);
                        ExcelApplication.DisplayException(message, ex);
                    }
                }
            }
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.RemoveViews"/> </summary> 
        public void RemoveViews(IList<IExcelTemplateView> views)
        {
            if (views != null)
            {
                try
                {
                    lock (syncRoot)
                    {
                        bool success = true;
                        foreach (IExcelTemplateView view in views)
                        {
                            try { RemoveView(view); }
                            catch { success = false; }
                        }
                        if (!success)
                            throw new EasyToolkitException("No all views have been removed. Please check the logs.");
                    }
                }
                catch (Exception ex)
                {
                    string message = "'Remove views' failed.";
                    Logger.Instance.LogException(LogType.Error, ex, message);
                    ExcelApplication.DisplayException(message, ex);
                }
            }
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.GetSheetTemplateViews"/> </summary> 
        public List<IExcelTemplateView> GetSheetTemplateViews(Excel.Worksheet sheet)
        {
            try
            {
                List<IExcelTemplateView> iViews = new List<IExcelTemplateView>();
                if (sheet != null)
                {
                    lock (syncRoot)
                    {
                        List<ExcelTemplateView> views = null;
                        if (viewsBySheet.TryGetValue(sheet, out views))
                        {
                            IEnumerable<ITemplateView> templateViews = BindingTemplateManager.Instance.GetAllViews().Where(v => views.Contains(v) && v is ExcelTemplateView);
                            iViews.AddRange(templateViews.Cast<IExcelTemplateView>());
                        }
                    }
                }
                return iViews;
            }
            catch (Exception ex)
            {
                string message = "'GetSheetTemplates' failed";
                Logger.Instance.LogException(LogType.Error, ex, message);
                ExcelApplication.DisplayException(message, ex);
                return null;
            }
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.Render"/> </summary> 
        public void Render(IExcelTemplateView view)
        {
            if (view != null)
                Render(new IExcelTemplateView[] { view });
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.Render"/> </summary> 
        public void Render(IEnumerable<IExcelTemplateView> views)
        {
            if (views != null)
            {
                views = views.Where(v => v != null);
                if (views.Count() > 0)
                {
                    try
                    {
                        lock (syncRoot)
                        {
                            Excel.Application application = ((views.First()) as ExcelTemplateView).FirstOutputCell.Application;
                            if (ExcelApplication.IsInEditMode())
                                ExcelApplication.DisplayMessageBox("'Render' is not allowed: Excel is in Edit mode", Forms.MessageBoxIcon.Warning);
                            else
                            {
                                using (FreezeExcel freezeExcel = new FreezeExcel(application))
                                {
                                    foreach (IExcelTemplateView view in views)
                                        ((ExcelTemplateView)view).Render();
                                }
                            }
                            application = null;
                        }
                    }
                    catch (Exception ex)
                    {
                        string message = "'Render' failed.";
                        Logger.Instance.LogException(LogType.Error, ex, message);
                        ExcelApplication.DisplayException(message, ex);
                    }
                }
            }
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.RenderDataOnly"/> </summary> 
        public void RenderDataOnly(IExcelTemplateView view)
        {
            if (view != null)
                RenderDataOnly(new IExcelTemplateView[] { view });
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.RenderDataOnly"/> </summary> 
        public void RenderDataOnly(IEnumerable<IExcelTemplateView> views)
        {
            if (views != null)
            {
                views = views.Where(v => v != null);
                if (views.Count() > 0)
                {
                    try
                    {
                        lock (syncRoot)
                        {
                            if (ExcelApplication.IsInEditMode())
                                ExcelApplication.DisplayMessageBox("'Render data only' is not allowed: Excel is in Edit mode", Forms.MessageBoxIcon.Warning);
                            else
                            {
                                Excel.Application application = ((views.First()) as ExcelTemplateView).FirstOutputCell.Application;
                                using (FreezeExcel freezeExcel = new FreezeExcel(application))
                                {
                                    foreach (IExcelTemplateView view in views)
                                        ((ExcelTemplateView)view).RenderDataOnly();
                                }
                                application = null;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string message = "'RenderDataOnly' failed.";
                        Logger.Instance.LogException(LogType.Error, ex, message);
                        ExcelApplication.DisplayException(message, ex);
                    }
                }
            }
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.ClearView"/> </summary> 
        public void ClearView(IExcelTemplateView view)
        {
            if (view != null)
                ClearViews(new IExcelTemplateView[] { view });
        }

        /// <summary> Implements <see cref="IExcelBindingTemplateManager.ClearViews"/> </summary> 
        public void ClearViews(IEnumerable<IExcelTemplateView> views)
        {
            if (views != null)
            {
                views = views.Where(v => v != null);
                if (views.Count() > 0)
                {
                    try
                    {
                        lock (syncRoot)
                        {
                            if (ExcelApplication.IsInEditMode())
                                ExcelApplication.DisplayMessageBox("'Clear views' is not allowed: Excel is in Edit mode", Forms.MessageBoxIcon.Warning);
                            else
                            {
                                Excel.Application application = ((views.First()) as ExcelTemplateView).FirstOutputCell.Application;
                                using (FreezeExcel freezeExcel = new FreezeExcel(application))
                                {
                                    foreach (IExcelTemplateView view in views)
                                    {
                                        if (view != null)
                                            ((ExcelTemplateView)view).Clear();
                                    }
                                }
                                application = null;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string message = "'Clear views' failed.";
                        Logger.Instance.LogException(LogType.Error, ex, message);
                        ExcelApplication.DisplayException(message, ex);
                    }
                }
            }
        }
        #endregion

        public void Dispose()
        {
            lock (syncRoot)
            {
                if (!disposed)
                {
                    disposed = true;
                    if (initDone)
                        ContextualMenuManager.Instance.ContextualMenusManager -= ManageContextualMenu;

                    ExcelNotifyPropertyManager.Dispose();
                    ExcelApplication.Dispose();
                    ExcelNotifyPropertyManager = null;
                    ExcelApplication = null;
                }
            }
        }
    }
}
